## @ GenCfgData.py
#
# Copyright (c) 2014 - 2019, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##

import os
import re
import sys
import struct
import marshal
from   functools import reduce
from   datetime import date

# Generated file copyright header

__copyright_tmp__ = """/** @file

  Platform Configuration %s File.

  Copyright (c) %4d, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

  This file is automatically generated. Please do NOT modify !!!

**/
"""

def Bytes2Val (Bytes):
    return reduce(lambda x,y: (x<<8)|y,  Bytes[::-1] )

def Bytes2Str (Bytes):
    return '{ %s }' % (', '.join('0x%02X' % i for i in Bytes))

def Str2Bytes (Value, Blen):
    Result = bytearray(Value[1:-1], 'utf-8')  # Excluding quotes
    if len(Result) < Blen:
        Result.extend(b'\x00' * (Blen - len(Result)))
    return Result

def Val2Bytes (Value, Blen):
    return [(Value>>(i*8) & 0xff) for i in range(Blen)]

def Array2Val (ValStr):
    ValStr = ValStr.strip()
    if ValStr.startswith('{'):
        ValStr = ValStr[1:]
    if ValStr.endswith('}'):
        ValStr = ValStr[:-1]
    if ValStr.startswith("'"):
        ValStr = ValStr[1:]
    if ValStr.endswith("'"):
        ValStr = ValStr[:-1]
    Value = 0
    for Each in ValStr.split(',')[::-1]:
        Each = Each.strip()
        if Each.startswith('0x'):
            Base = 16
        else:
            Base = 10
        Value = (Value << 8) | int(Each, Base)
    return Value

def GetCopyrightHeader (FileType, AllowModify = False):
    FileDescription = {
        'bsf' : 'Boot Setting',
        'dsc' : 'Definition',
        'dlt' : 'Delta',
        'inc' : 'C Binary Blob',
        'h'   : 'C Struct Header'
    }
    if FileType in ['bsf', 'dsc', 'dlt']:
        CommentChar = '#'
    else:
        CommentChar = ''
    Lines = __copyright_tmp__.split('\n')

    if AllowModify:
      Lines = [Line for Line in Lines if 'Please do NOT modify' not in Line]

    CopyrightHdr = '\n'.join('%s%s' % (CommentChar, Line) for Line in Lines)[:-1] + '\n'

    return CopyrightHdr % (FileDescription[FileType], date.today().year)


class CLogicalExpression:
    def __init__(self):
        self.index    = 0
        self.string   = ''
        self.dictVariable = {}
        self.parenthesisOpenSet   =  '('
        self.parenthesisCloseSet  =  ')'

    def errExit(self, err = ''):
        print ("ERROR: Express parsing for:")
        print ("       %s" % self.string)
        print ("       %s^" % (' ' * self.index))
        if err:
            print ("INFO : %s" % err)
        raise Exception ("Logical expression parsing error!")

    def getNonNumber (self, n1, n2):
        if not n1.isdigit():
            return n1
        if not n2.isdigit():
            return n2
        return None

    def getCurr(self, lens = 1):
        try:
            if lens == -1:
                return self.string[self.index :]
            else:
                if self.index + lens > len(self.string):
                    lens = len(self.string) - self.index
                return self.string[self.index : self.index + lens]
        except Exception:
            return ''

    def isLast(self):
        return self.index == len(self.string)

    def moveNext(self, len = 1):
        self.index += len

    def skipSpace(self):
        while not self.isLast():
            if self.getCurr() in ' \t':
                self.moveNext()
            else:
                return

    def getNumber(self, var):
        var = var.strip()
        if   re.match('^0x[a-fA-F0-9]+$', var):
            value = int(var, 16)
        elif re.match('^0b[01]+$', var):
            value = int(var, 2)
        elif re.match('^[+-]?\d+$', var):
            value = int(var, 10)
        else:
            self.errExit("Invalid value '%s'" % var)
        return value

    def getVariable(self, var):
        value = self.dictVariable.get(var, None)
        if value == None:
            self.errExit("Unrecognized variable '%s'" % var)
        return value

    def parseValue(self):
        self.skipSpace()
        var = ''
        while not self.isLast():
            char = self.getCurr()
            if re.match('^[\w.]', char):
                var += char
                self.moveNext()
            else:
                break

        if len(var):
          if var[0].isdigit():
              value = self.getNumber(var)
          else:
              value = self.getVariable(var)
        else:
          self.errExit('Invalid number or variable found !')

        return int(value)

    def parseSingleOp(self):
        self.skipSpace()
        char = self.getCurr()
        if char == '~':
            self.moveNext()
            return ~self.parseBrace()
        else:
            return self.parseValue()

    def parseBrace(self):
        self.skipSpace()
        char = self.getCurr()
        parenthesisType = self.parenthesisOpenSet.find(char)
        if parenthesisType >= 0:
            self.moveNext()
            value = self.parseExpr()
            self.skipSpace()
            if self.getCurr() != self.parenthesisCloseSet[parenthesisType]:
                self.errExit ("No closing brace !")
            self.moveNext()
            if parenthesisType   == 1:  # [ : Get content
                value = self.getContent(value)
            elif parenthesisType == 2:  # { : To  address
                value = self.toAddress(value)
            elif parenthesisType == 3:  # < : To  offset
                value = self.toOffset(value)
            return value
        else:
            return self.parseSingleOp()

    def parseMul(self):
        values = [self.parseBrace()]
        ops    = ['*']
        while True:
            self.skipSpace()
            char = self.getCurr()
            if char == '*':
                self.moveNext()
                values.append(self.parseBrace())
                ops.append(char)
            elif char == '/':
                self.moveNext()
                values.append(self.parseBrace())
                ops.append(char)
            else:
                break
        value  = 1
        for idx, each in enumerate(values):
            if ops[idx] == '*':
                value  *= each
            else:
                value //= each
        return value

    def parseAndOr(self):
        value  = self.parseMul()
        op     = None
        while True:
            self.skipSpace()
            char = self.getCurr()
            if char == '&':
                self.moveNext()
                value &= self.parseMul()
            elif char == '|':
                div_index = self.index
                self.moveNext()
                value |= self.parseMul()
            else:
                break

        return value

    def parseAddMinus(self):
        values = [self.parseAndOr()]
        while True:
            self.skipSpace()
            char = self.getCurr()
            if char == '+':
                self.moveNext()
                values.append(self.parseAndOr())
            elif char == '-':
                self.moveNext()
                values.append(-1 * self.parseAndOr())
            else:
                break
        return sum(values)

    def parseExpr(self):
        return self.parseAddMinus()

    def evaluateExpress (self, Expr, VarDict = {}):
        self.index        = 0
        self.string       = Expr
        self.dictVariable = VarDict
        Result = self.parseExpr()
        return Result

class CGenCfgData:
    def __init__(self):
        self.Debug          = False
        self.Error          = ''
        self.ReleaseMode    = True

        self._GlobalDataDef = """
GlobalDataDef
    SKUID = 0, "DEFAULT"
EndGlobalData

"""
        self._BuidinOptionTxt = """
List &EN_DIS
    Selection 0x1 , "Enabled"
    Selection 0x0 , "Disabled"
EndList

"""
        self._StructType    = ['UINT8','UINT16','UINT32','UINT64']
        self._BsfKeyList    = ['FIND','NAME','HELP','TYPE','PAGE', 'PAGES', 'BLOCK', 'OPTION','CONDITION','ORDER', 'MARKER', 'SUBT']
        self._HdrKeyList    = ['HEADER','STRUCT', 'EMBED', 'COMMENT']
        self._BuidinOption  = {'$EN_DIS' : 'EN_DIS'}

        self._MacroDict   = {}
        self._VarDict     = {}
        self._PcdsDict    = {}
        self._CfgBlkDict  = {}
        self._CfgPageDict = {}
        self._CfgOptsDict = {}
        self._BsfTempDict = {}
        self._CfgItemList = []
        self._DscLines    = []
        self._DscFile     = ''
        self._CfgPageTree = {}

        self._MapVer      = 0
        self._MinCfgTagId = 0x100

    def ParseMacros (self, MacroDefStr):
        # ['-DABC=1', '-D', 'CFG_DEBUG=1', '-D', 'CFG_OUTDIR=Build']
        self._MacroDict = {}
        IsExpression = False
        for Macro in MacroDefStr:
            if Macro.startswith('-D'):
                IsExpression = True
                if len(Macro) > 2:
                    Macro = Macro[2:]
                else :
                    continue
            if IsExpression:
                IsExpression = False
                Match = re.match("(\w+)=(.+)", Macro)
                if Match:
                    self._MacroDict[Match.group(1)] = Match.group(2)
                else:
                    Match = re.match("(\w+)", Macro)
                    if Match:
                        self._MacroDict[Match.group(1)] = ''
        if len(self._MacroDict) == 0:
            Error = 1
        else:
            Error = 0
            if self.Debug:
                print ("INFO : Macro dictionary:")
                for Each in self._MacroDict:
                    print ("       $(%s) = [ %s ]" % (Each , self._MacroDict[Each]))
        return Error

    def EvaulateIfdef   (self, Macro):
        Result = Macro in self._MacroDict
        if self.Debug:
            print ("INFO : Eval Ifdef [%s] : %s" % (Macro, Result))
        return  Result

    def ExpandMacros (self, Input, Preserve = False):
        Line = Input
        Match = re.findall("\$\(\w+\)", Input)
        if Match:
            for Each in Match:
              Variable = Each[2:-1]
              if Variable in self._MacroDict:
                  Line = Line.replace(Each, self._MacroDict[Variable])
              else:
                  if self.Debug:
                      print ("WARN : %s is not defined" % Each)
                  if not Preserve:
                      Line = Line.replace(Each, Each[2:-1])
        return Line

    def ExpandPcds (self, Input):
        Line = Input
        Match = re.findall("(\w+\.\w+)", Input)
        if Match:
            for PcdName in Match:
              if PcdName in self._PcdsDict:
                  Line = Line.replace(PcdName, self._PcdsDict[PcdName])
              else:
                  if self.Debug:
                      print ("WARN : %s is not defined" % PcdName)
        return Line

    def EvaluateExpress (self, Expr):
        ExpExpr = self.ExpandPcds(Expr)
        ExpExpr = self.ExpandMacros(ExpExpr)
        LogExpr = CLogicalExpression()
        Result  = LogExpr.evaluateExpress (ExpExpr, self._VarDict)
        if self.Debug:
            print ("INFO : Eval Express [%s] : %s" % (Expr, Result))
        return Result

    def ValueToByteArray (self, ValueStr, Length):
        Match = re.match("\{\s*FILE:(.+)\}", ValueStr)
        if Match:
          FileList = Match.group(1).split(',')
          Result  = bytearray()
          for File in FileList:
            File = File.strip()
            BinPath = os.path.join(os.path.dirname(self._DscFile), File)
            Result.extend(bytearray(open(BinPath, 'rb').read()))
        else:
            try:
                Result  = bytearray(self.ValueToList(ValueStr, Length))
            except ValueError as e:
                raise Exception ("Bytes in '%s' must be in range 0~255 !" % ValueStr)
        if len(Result) < Length:
            Result.extend(b'\x00' * (Length - len(Result)))
        elif len(Result) > Length:
            raise Exception ("Value '%s' is too big to fit into %d bytes !" % (ValueStr, Length))

        return Result[:Length]

    def ValueToList (self, ValueStr, Length):
        if ValueStr[0] == '{':
            Result = []
            BinList = ValueStr[1:-1].split(',')
            InBitField     = False
            LastInBitField = False
            Value          = 0
            BitLen         = 0
            for Element in BinList:
                InBitField = False
                Each = Element.strip()
                if len(Each) == 0:
                    pass
                else:
                    if Each[0] in ['"', "'"]:
                        Result.extend(list(bytearray(Each[1:-1], 'utf-8')))
                    elif ':' in Each:
                        Match    = re.match("(.+):(\d+)b", Each)
                        if Match is None:
                            raise Exception("Invald value list format '%s' !" % Each)
                        InBitField = True
                        CurrentBitLen = int(Match.group(2))
                        CurrentValue  = ((self.EvaluateExpress(Match.group(1)) & (1<<CurrentBitLen) - 1)) << BitLen
                    else:
                        Result.append(self.EvaluateExpress(Each.strip()))
                if InBitField:
                    Value  += CurrentValue
                    BitLen += CurrentBitLen
                if LastInBitField and ((not InBitField) or (Element == BinList[-1])):
                    if BitLen % 8 != 0:
                        raise Exception("Invald bit field length!")
                    Result.extend(Val2Bytes(Value, BitLen // 8))
                    Value  = 0
                    BitLen = 0
                LastInBitField = InBitField
        elif ValueStr.startswith("'") and ValueStr.endswith("'"):
            Result = Str2Bytes (ValueStr, Length)
        elif ValueStr.startswith('"') and ValueStr.endswith('"'):
            Result = Str2Bytes (ValueStr, Length)
        else:
            Result = Val2Bytes (self.EvaluateExpress(ValueStr), Length)
        return Result

    def FormatDeltaValue(self, ConfigDict):
        ValStr = ConfigDict['value']
        if ValStr[0] == "'":
            # Remove padding \x00 in the value string
            ValStr = "'%s'" % ValStr[1:-1].rstrip('\x00')

        Struct = ConfigDict['struct']
        if Struct in self._StructType:
            # Format the array using its struct type
            Unit   = int(Struct[4:]) // 8
            Value  = Array2Val(ConfigDict['value'])
            Loop   = ConfigDict['length'] // Unit
            Values = []
            for Each in range(Loop):
                Values.append (Value & ((1 << (Unit * 8)) - 1))
                Value = Value >> (Unit * 8)
            ValStr = '{ ' + ', '.join ([('0x%%0%dX' % (Unit * 2)) % x for x in Values]) + ' }'

        return ValStr

    def FormatListValue(self, ConfigDict):
        Struct = ConfigDict['struct']
        if Struct not in self._StructType:
            return

        DataList = self.ValueToList(ConfigDict['value'], ConfigDict['length'])
        Unit = int(Struct[4:]) // 8
        if int(ConfigDict['length']) != Unit * len(DataList):
            # Fallback to byte array
            Unit = 1
            if int(ConfigDict['length']) != len(DataList):
                raise Exception("Array size is not proper for '%s' !" % ConfigDict['cname'])

        ByteArray = []
        for Value in DataList:
            for Loop in range(Unit):
                ByteArray.append("0x%02X" % (Value & 0xFF))
                Value = Value >> 8
        NewValue  = '{'  + ','.join(ByteArray) + '}'
        ConfigDict['value'] = NewValue

        return ""

    def GetOrderNumber (self, Offset, Order, BitOff = 0):
        if isinstance(Order, int):
            if Order == -1:
                Order = Offset << 16
        else:
            (Major, Minor) = Order.split('.')
            Order = (int (Major, 16) << 16) + ((int (Minor, 16) & 0xFF) << 8)
        return Order + (BitOff & 0xFF)

    def SubtituteLine (self, Line, Args):
        Args = Args.strip()
        Vars = Args.split(':')
        Line = self.ExpandMacros(Line, True)
        for Idx in range(len(Vars)-1, 0, -1):
            Line = Line.replace('$(%d)' % Idx, Vars[Idx].strip())
        Remaining = re.findall ('\$\(\d+\)', Line)
        if len(Remaining) > 0:
            raise Exception ("ERROR: Unknown argument '%s' for template '%s' !" % (Remaining[0], Vars[0]))
        return Line

    def CfgDuplicationCheck (self, CfgDict, Name):
        if not self.Debug:
            return

        if Name == 'Dummy':
            return

        if Name not in CfgDict:
            CfgDict[Name] = 1
        else:
            print ("WARNING: Duplicated item found '%s' !" % ConfigDict['cname'])

    def AddBsfChildPage (self, Child, Parent = 'root'):
        def AddBsfChildPageRecursive (PageTree, Parent, Child):
            Key = next(iter(PageTree))
            if Parent == Key:
                PageTree[Key].append({Child : []})
                return True
            else:
                Result = False
                for Each in PageTree[Key]:
                    if AddBsfChildPageRecursive (Each, Parent, Child):
                        Result = True
                        break
                return Result

        return AddBsfChildPageRecursive (self._CfgPageTree, Parent, Child)

    def ParseDscFile (self, DscFile):
        self._DscLines    = []
        self._CfgItemList = []
        self._CfgPageDict = {}
        self._CfgBlkDict  = {}
        self._BsfTempDict = {}
        self._CfgPageTree = {'root' : []}
        self._DscFile     = DscFile

        CfgDict = {}

        SectionNameList = ["Defines".lower(), "PcdsFeatureFlag".lower(),
                           "PcdsDynamicVpd.Tmp".lower(), "PcdsDynamicVpd.Upd".lower()]

        IsDefSect       = False
        IsPcdSect       = False
        IsUpdSect       = False
        IsTmpSect       = False

        TemplateName    = ''

        IfStack         = []
        ElifStack       = []
        Error           = 0
        ConfigDict      = {}

        DscFd        = open(DscFile, "r")
        DscLines     = DscFd.readlines()
        DscFd.close()

        BsfRegExp    = re.compile("(%s):{(.+?)}(?:$|\s+)" % '|'.join(self._BsfKeyList))
        HdrRegExp    = re.compile("(%s):{(.+?)}" % '|'.join(self._HdrKeyList))
        CfgRegExp    = re.compile("^([_a-zA-Z0-9]+)\s*\|\s*(0x[0-9A-F]+|\*)\s*\|\s*(\d+|0x[0-9a-fA-F]+)\s*\|\s*(.+)")
        TksRegExp    = re.compile("^(g[_a-zA-Z0-9]+\.)(.+)")
        SkipLines = 0
        while len(DscLines):
            DscLine  = DscLines.pop(0).strip()
            if SkipLines == 0:
              self._DscLines.append (DscLine)
            else:
              SkipLines = SkipLines - 1
            if len(DscLine) == 0:
              continue

            Handle   = False
            Match    = re.match("^\[(.+)\]", DscLine)
            if Match is not None:
                IsDefSect = False
                IsPcdSect = False
                IsUpdSect = False
                IsTmpSect = False
                SectionName = Match.group(1).lower()
                if  SectionName  == SectionNameList[0]:
                    IsDefSect = True
                if  SectionName  == SectionNameList[1]:
                    IsPcdSect = True
                elif SectionName == SectionNameList[2]:
                    IsTmpSect = True
                elif SectionName == SectionNameList[3]:
                    ConfigDict = {
                        'header'    : 'ON',
                        'page'      : '',
                        'name'      : '',
                        'find'      : '',
                        'struct'    : '',
                        'embed'     : '',
                        'marker'    : '',
                        'option'    : '',
                        'comment'   : '',
                        'condition' : '',
                        'order'     : -1,
                        'subreg'    : []
                    }
                    IsUpdSect = True
                    Offset    = 0
            else:
                if IsDefSect or IsPcdSect or IsUpdSect or IsTmpSect:
                    Match = False if DscLine[0] != '!' else True
                    if Match:
                        Match = re.match("^!(else|endif|ifdef|ifndef|if|elseif|include)\s*(.+)?$", DscLine)
                    Keyword   = Match.group(1) if Match else ''
                    Remaining = Match.group(2) if Match else ''
                    Remaining = '' if Remaining is None else Remaining.strip()

                    if Keyword in ['if', 'elseif', 'ifdef', 'ifndef', 'include'] and not Remaining:
                        raise Exception ("ERROR: Expression is expected after '!if' or !elseif' for line '%s'" % DscLine)

                    if Keyword == 'else':
                        if IfStack:
                            IfStack[-1] = not IfStack[-1]
                        else:
                            raise Exception ("ERROR: No paired '!if' found for '!else' for line '%s'" % DscLine)
                    elif Keyword == 'endif':
                        if IfStack:
                            IfStack.pop()
                            Level = ElifStack.pop()
                            if Level > 0:
                                del IfStack[-Level:]
                        else:
                            raise Exception ("ERROR: No paired '!if' found for '!endif' for line '%s'" % DscLine)
                    elif Keyword == 'ifdef' or Keyword == 'ifndef':
                        Result = self.EvaulateIfdef (Remaining)
                        if Keyword == 'ifndef':
                            Result = not Result
                        IfStack.append(Result)
                        ElifStack.append(0)
                    elif Keyword == 'if' or Keyword == 'elseif':
                        Result = self.EvaluateExpress(Remaining)
                        if Keyword == "if":
                            ElifStack.append(0)
                            IfStack.append(Result)
                        else:   #elseif
                            if IfStack:
                                IfStack[-1] = not IfStack[-1]
                                IfStack.append(Result)
                                ElifStack[-1] = ElifStack[-1] + 1
                            else:
                                raise Exception ("ERROR: No paired '!if' found for '!elif' for line '%s'" % DscLine)
                    else:
                        if IfStack:
                            Handle = reduce(lambda x,y: x and y, IfStack)
                        else:
                            Handle = True
                        if Handle:
                            if Keyword == 'include':
                                Remaining = self.ExpandMacros(Remaining)
                                # Relative to DSC filepath
                                IncludeFilePath = os.path.join(os.path.dirname(DscFile), Remaining)
                                if not os.path.exists(IncludeFilePath):
                                    # Relative to repository to find dsc in common platform
                                    IncludeFilePath = os.path.abspath(os.path.join(os.path.dirname(DscFile), "../../..", Remaining))

                                try:
                                    IncludeDsc  = open(IncludeFilePath, "r")
                                except:
                                    raise Exception ("ERROR: Cannot open file '%s'." % IncludeFilePath)
                                NewDscLines = IncludeDsc.readlines()
                                IncludeDsc.close()
                                StartTag = ['# !< include %s\n' % Remaining]
                                EndTag   = ['# !> include %s\n' % Remaining]
                                DscLines = StartTag + NewDscLines + EndTag + DscLines
                                del self._DscLines[-1]
                            else:
                                if DscLine.startswith('!'):
                                    raise Exception ("ERROR: Unrecoginized directive for line '%s'" % DscLine)

            if not Handle:
                continue

            if IsDefSect:
                Match = re.match("^\s*(?:DEFINE\s+)*(\w+)\s*=\s*(.+)", DscLine)
                if Match:
                    self._MacroDict[Match.group(1)] = Match.group(2)
                    if self.Debug:
                        print ("INFO : DEFINE %s = [ %s ]" % (Match.group(1), Match.group(2)))

            elif IsPcdSect:
                Match = re.match("^\s*([\w\.]+)\s*\|\s*(\w+)", DscLine)
                if Match:
                    self._PcdsDict[Match.group(1)] = Match.group(2)
                    if self.Debug:
                        print ("INFO : PCD %s = [ %s ]" % (Match.group(1), Match.group(2)))

            elif IsTmpSect:
                # !BSF DEFT:{GPIO_TMPL:START}
                Match = re.match("^\s*#\s+(!BSF)\s+DEFT:{(.+?):(START|END)}", DscLine)
                if Match:
                    if Match.group(3) == 'START' and not TemplateName:
                        TemplateName = Match.group(2).strip()
                        self._BsfTempDict[TemplateName] = []
                    if Match.group(3) == 'END' and (TemplateName == Match.group(2).strip()) and TemplateName:
                        TemplateName = ''
                else:
                    if TemplateName:
                        Match = re.match("^!include\s*(.+)?$", DscLine)
                        if Match:
                            continue
                        self._BsfTempDict[TemplateName].append(DscLine)

            else:
                Match = re.match("^\s*#\s+(!BSF|!HDR)\s+(.+)", DscLine)
                if Match:
                    Remaining = Match.group(2)
                    if Match.group(1) == '!BSF':
                        Result = BsfRegExp.findall (Remaining)
                        if Result:
                            for Each in Result:
                                Key       = Each[0]
                                Remaining = Each[1]

                                if   Key   == 'BLOCK':
                                    Match = re.match("NAME:\"(.+)\"\s*,\s*VER:\"(.+)\"\s*", Remaining)
                                    if Match:
                                        self._CfgBlkDict['name'] = Match.group(1)
                                        self._CfgBlkDict['ver']  = Match.group(2)

                                elif Key == 'SUBT':
                                    #GPIO_TMPL:1:2:3
                                    Remaining = Remaining.strip()
                                    Match = re.match("(\w+)\s*:", Remaining)
                                    if Match:
                                        TemplateName = Match.group(1)
                                        for Line in self._BsfTempDict[TemplateName][::-1]:
                                            NewLine = self.SubtituteLine (Line, Remaining)
                                            DscLines.insert(0, NewLine)
                                            SkipLines += 1

                                elif Key   == 'PAGES':
                                    # !BSF PAGES:{HSW:"Haswell System Agent", LPT:"Lynx Point PCH"}
                                    PageList = Remaining.split(',')
                                    for Page in PageList:
                                        Page  = Page.strip()
                                        Match = re.match('(\w+):(\w*:)?\"(.+)\"', Page)
                                        if Match:
                                            PageName   = Match.group(1)
                                            ParentName = Match.group(2)
                                            if not ParentName or ParentName == ':' :
                                                ParentName = 'root'
                                            else:
                                                ParentName = ParentName[:-1]
                                            if not self.AddBsfChildPage (PageName, ParentName):
                                                raise Exception("Cannot find parent page '%s'!" % ParentName)
                                            self._CfgPageDict[PageName] = Match.group(3)
                                        else:
                                            raise Exception("Invalid page definitions '%s'!" % Page)

                                elif Key in ['NAME', 'HELP', 'OPTION'] and Remaining.startswith('+'):
                                    # Allow certain options to be extended to multiple lines
                                    ConfigDict[Key.lower()] += Remaining[1:]

                                else:
                                    if Key == 'NAME':
                                        Remaining = Remaining.strip()
                                    elif Key == 'CONDITION':
                                        Remaining = self.ExpandMacros(Remaining.strip())
                                    ConfigDict[Key.lower()]  = Remaining
                    else:
                        Match = HdrRegExp.match(Remaining)
                        if Match:
                            Key = Match.group(1)
                            Remaining = Match.group(2)
                            if Key  == 'EMBED':
                                Parts = Remaining.split(':')
                                Names = Parts[0].split(',')
                                DummyDict = ConfigDict.copy()
                                if len(Names) > 1:
                                    Remaining = Names[0] + ':' + ':'.join(Parts[1:])
                                    DummyDict['struct'] = Names[1]
                                else:
                                    DummyDict['struct'] = Names[0]
                                DummyDict['cname']   = 'Dummy'
                                DummyDict['name']    = ''
                                DummyDict['embed']   = Remaining
                                DummyDict['offset']  = Offset
                                DummyDict['length']  = 0
                                DummyDict['value']   = '0'
                                DummyDict['type']    = 'Reserved'
                                DummyDict['help']    = ''
                                DummyDict['subreg']  = []
                                self._CfgItemList.append(DummyDict)
                            else:
                                ConfigDict[Key.lower()] = Remaining
                # Check CFG line
                #   gCfgData.VariableName   |    * | 0x01 | 0x1
                Clear = False

                Match = TksRegExp.match (DscLine)
                if Match:
                    DscLine = 'gCfgData.%s' % Match.group(2)

                if DscLine.startswith('gCfgData.'):
                    Match = CfgRegExp.match(DscLine[9:])
                else:
                    Match = None
                if Match:
                    ConfigDict['space']  = 'gCfgData'
                    ConfigDict['cname']  = Match.group(1)
                    if Match.group(2) != '*':
                        Offset =  int (Match.group(2), 16)
                    ConfigDict['offset'] = Offset
                    ConfigDict['order']  = self.GetOrderNumber (ConfigDict['offset'], ConfigDict['order'])

                    Value = Match.group(4).strip()
                    if Match.group(3).startswith("0x"):
                        Length  = int (Match.group(3), 16)
                    else :
                        Length  = int (Match.group(3))

                    Offset += Length

                    ConfigDict['length'] = Length
                    Match = re.match("\$\((\w+)\)", Value)
                    if Match:
                        if Match.group(1) in self._MacroDict:
                            Value = self._MacroDict[Match.group(1)]

                    ConfigDict['value']  = Value
                    if re.match("\{\s*FILE:(.+)\}", Value):
                        # Expand embedded binary file
                        ValArray = self.ValueToByteArray (ConfigDict['value'], ConfigDict['length'])
                        NewValue = Bytes2Str(ValArray)
                        self._DscLines[-1] = re.sub(r'(.*)(\{\s*FILE:.+\})' , r'\1 %s' % NewValue, self._DscLines[-1])
                        ConfigDict['value']  = NewValue

                    if ConfigDict['name']  == '':
                        # Clear BSF specific items
                        ConfigDict['bsfname'] = ''
                        ConfigDict['help']   = ''
                        ConfigDict['type']   = ''
                        ConfigDict['option'] = ''

                    self.CfgDuplicationCheck (CfgDict, ConfigDict['cname'])
                    self._CfgItemList.append(ConfigDict.copy())
                    Clear = True

                else:
                    # It could be a virtual item as below
                    # !BSF FIELD:{SerialDebugPortAddress0:1}
                    # or
                    # @Bsf FIELD:{SerialDebugPortAddress0:1b}
                    Match = re.match("^\s*#\s+(!BSF)\s+FIELD:{(.+)}", DscLine)
                    if Match:
                        BitFieldTxt = Match.group(2)
                        Match = re.match("(.+):(\d+)b([BWDQ])?", BitFieldTxt)
                        if not Match:
                            raise Exception ("Incorrect bit field format '%s' !" % BitFieldTxt)
                        UnitBitLen = 1
                        SubCfgDict = ConfigDict.copy()
                        SubCfgDict['cname']  = Match.group(1)
                        SubCfgDict['bitlength'] = int (Match.group(2)) * UnitBitLen
                        if SubCfgDict['bitlength'] > 0:
                            LastItem =  self._CfgItemList[-1]
                            if len(LastItem['subreg']) == 0:
                                SubOffset  = 0
                            else:
                                SubOffset  = LastItem['subreg'][-1]['bitoffset'] + LastItem['subreg'][-1]['bitlength']
                            if Match.group(3) == 'B':
                                SubCfgDict['bitunit'] = 1
                            elif Match.group(3) == 'W':
                                SubCfgDict['bitunit'] = 2
                            elif Match.group(3) == 'Q':
                                SubCfgDict['bitunit'] = 8
                            else:
                                SubCfgDict['bitunit'] = 4
                            SubCfgDict['bitoffset'] = SubOffset
                            SubCfgDict['order'] = self.GetOrderNumber (SubCfgDict['offset'], SubCfgDict['order'], SubOffset)
                            SubCfgDict['value'] = ''
                            SubCfgDict['cname'] = '%s_%s' % (LastItem['cname'], Match.group(1))
                            self.CfgDuplicationCheck (CfgDict, SubCfgDict['cname'])
                            LastItem['subreg'].append (SubCfgDict.copy())
                        Clear = True

                if Clear:
                    ConfigDict['name']      = ''
                    ConfigDict['find']      = ''
                    ConfigDict['struct']    = ''
                    ConfigDict['embed']     = ''
                    ConfigDict['marker']    = ''
                    ConfigDict['comment']   = ''
                    ConfigDict['order']     = -1
                    ConfigDict['subreg']    = []
                    ConfigDict['option']    = ''
                    ConfigDict['condition'] = ''

        return Error

    def GetBsfBitFields (self, subitem, bytes):
        start = subitem['bitoffset']
        end   = start + subitem['bitlength']
        bitsvalue = ''.join('{0:08b}'.format(i) for i in bytes[::-1])
        bitsvalue = bitsvalue[::-1]
        bitslen   = len(bitsvalue)
        if start > bitslen or end > bitslen:
            raise Exception ("Invalid bits offset [%d,%d] %d for %s" % (start, end, bitslen, subitem['name']))
        return '0x%X' % (int(bitsvalue[start:end][::-1], 2))

    def UpdateBsfBitFields (self, SubItem, NewValue, ValueArray):
        Start = SubItem['bitoffset']
        End   = Start + SubItem['bitlength']
        Blen  = len (ValueArray)
        BitsValue = ''.join('{0:08b}'.format(i) for i in ValueArray[::-1])
        BitsValue = BitsValue[::-1]
        BitsLen   = len(BitsValue)
        if Start > BitsLen or End > BitsLen:
            raise Exception ("Invalid bits offset [%d,%d] %d for %s" % (Start, End, BitsLen, SubItem['name']))
        BitsValue = BitsValue[:Start] + '{0:0{1}b}'.format(NewValue, SubItem['bitlength'])[::-1] + BitsValue[End:]
        ValueArray[:]  = bytearray.fromhex('{0:0{1}x}'.format(int(BitsValue[::-1], 2), Blen * 2))[::-1]

    def CreateVarDict (self):
        Error = 0
        self._VarDict = {}
        if len(self._CfgItemList) > 0:
            Item = self._CfgItemList[-1]
            self._VarDict['_LENGTH_'] = '%d' % (Item['offset'] + Item['length'])
        for Item in self._CfgItemList:
            Embed = Item['embed']
            Match = re.match("^(\w+):(\w+):(START|END)", Embed)
            if Match:
                StructName = Match.group(1)
                VarName = '_%s_%s_' % (Match.group(3), StructName)
                if Match.group(3) == 'END':
                    self._VarDict[VarName] = Item['offset'] + Item['length']
                    self._VarDict['_LENGTH_%s_' % StructName] = \
                        self._VarDict['_END_%s_' % StructName] - self._VarDict['_START_%s_' % StructName]
                    if Match.group(2).startswith('TAG_'):
                        if self._VarDict['_LENGTH_%s_' % StructName] % 4:
                            raise Exception("Size of structure '%s' is %d, not DWORD aligned !" % (StructName, self._VarDict['_LENGTH_%s_' % StructName]))
                        self._VarDict['_TAG_%s_' % StructName] = int (Match.group(2)[4:], 16) & 0xFFF
                else:
                    self._VarDict[VarName] = Item['offset']
            if Item['marker']:
                self._VarDict['_OFFSET_%s_' % Item['marker'].strip()] = Item['offset']
        return Error

    def UpdateBsfBitUnit (self, Item):
        BitTotal  = 0
        BitOffset = 0
        StartIdx  = 0
        Unit      = None
        UnitDec   = {1:'BYTE', 2:'WORD', 4:'DWORD', 8:'QWORD'}
        for Idx, SubItem in enumerate(Item['subreg']):
            if Unit is None:
                Unit  = SubItem['bitunit']
            BitLength = SubItem['bitlength']
            BitTotal  += BitLength
            BitOffset += BitLength

            if BitOffset > 64 or BitOffset > Unit * 8:
                break

            if BitOffset == Unit * 8:
                for SubIdx in range (StartIdx, Idx + 1):
                    Item['subreg'][SubIdx]['bitunit'] = Unit
                BitOffset = 0
                StartIdx  = Idx + 1
                Unit      = None

        if BitOffset > 0:
            raise Exception ("Bit fields cannot fit into %s for '%s.%s' !" % (UnitDec[Unit], Item['cname'], SubItem['cname']))

        ExpectedTotal = Item['length'] * 8
        if Item['length'] * 8 != BitTotal:
            raise Exception ("Bit fields total length (%d) does not match length (%d) of '%s' !" % (BitTotal, ExpectedTotal, Item['cname']))

    def UpdateDefaultValue (self):
        Error = 0
        for Idx, Item in enumerate(self._CfgItemList):
            if len(Item['subreg']) == 0:
                Value = Item['value']
                if (len(Value) > 0) and (Value[0] == '{' or Value[0] == "'" or Value[0] == '"'):
                    # {XXX} or 'XXX' strings
                    self.FormatListValue(self._CfgItemList[Idx])
                else:
                  Match = re.match("(0x[0-9a-fA-F]+|[0-9]+)", Value)
                  if not Match:
                    NumValue = self.EvaluateExpress (Value)
                    Item['value'] = '0x%X' % NumValue
            else:
                ValArray = self.ValueToByteArray (Item['value'], Item['length'])
                for SubItem in Item['subreg']:
                    SubItem['value']   = self.GetBsfBitFields(SubItem, ValArray)
                self.UpdateBsfBitUnit (Item)
        return Error

    @staticmethod
    def ExpandIncludeFiles (FilePath, CurDir = ''):
        if CurDir == '':
            CurDir   = os.path.dirname(FilePath)
            FilePath = os.path.basename(FilePath)

        InputFilePath = os.path.join(CurDir, FilePath)
        File  = open(InputFilePath, "r")
        Lines = File.readlines()
        File.close()

        NewLines = []
        for LineNum, Line in enumerate(Lines):
            Match = re.match("^!include\s*(.+)?$", Line.strip())
            if Match:
                IncPath = Match.group(1)
                TmpPath = os.path.join(CurDir, IncPath)
                OrgPath = TmpPath
                if not os.path.exists(TmpPath):
                    CurDir = os.path.join(os.path.dirname (os.path.realpath(__file__)), "..", "..")
                TmpPath = os.path.join(CurDir, IncPath)
                if not os.path.exists(TmpPath):
                    raise Exception ("ERROR: Cannot open include file '%s'." % OrgPath)
                else:
                    NewLines.append (('# Included from file: %s\n' % IncPath, TmpPath, 0))
                    NewLines.append (('# %s\n' % ('=' * 80), TmpPath, 0))
                    NewLines.extend (CGenCfgData.ExpandIncludeFiles (IncPath, CurDir))
            else:
                NewLines.append ((Line, InputFilePath, LineNum))

        return NewLines

    def OverrideDefaultValue (self, DltFile):
        Error    = 0
        DltLines = CGenCfgData.ExpandIncludeFiles (DltFile);

        PlatformId  = None
        for Line, FilePath, LineNum in DltLines:
          Line = Line.strip()
          if not Line or Line.startswith('#'):
            continue
          Match = re.match("\s*(\w+)\.(\w+)(\.\w+)?\s*\|\s*(.+)", Line)
          if not Match:
            raise Exception("Unrecognized line '%s' (File:'%s' Line:%d) !" % (Line, FilePath, LineNum + 1))

          Found   = False
          InScope = False
          for Idx, Item in enumerate(self._CfgItemList):
            if not InScope:
              if not (Item['embed'].endswith(':START') and Item['embed'].startswith(Match.group(1))):
                continue
              InScope = True
            if Item['cname'] == Match.group(2):
              Found = True
              break
            if Item['embed'].endswith(':END') and Item['embed'].startswith(Match.group(1)):
              break
          Name = '%s.%s' % (Match.group(1),Match.group(2))
          if not Found:
              ErrItem = Match.group(2) if InScope else Match.group(1)
              raise Exception("Invalid configuration '%s' in '%s' (File:'%s' Line:%d) !" %
                    (ErrItem, Name, FilePath, LineNum + 1))

          ValueStr = Match.group(4).strip()
          if Match.group(3) is not None:
              # This is a subregion item
              BitField = Match.group(3)[1:]
              Found = False
              if len(Item['subreg']) > 0:
                  for SubItem in Item['subreg']:
                      if SubItem['cname'] == '%s_%s' % (Item['cname'], BitField):
                          Found = True
                          break
              if not Found:
                  raise Exception("Invalid configuration bit field '%s' in '%s.%s' (File:'%s' Line:%d) !" %
                        (BitField, Name, BitField, FilePath, LineNum + 1))

              try:
                  Value = int(ValueStr, 16) if ValueStr.startswith('0x') else int(ValueStr, 10)
              except:
                  raise Exception("Invalid value '%s' for bit field '%s.%s' (File:'%s' Line:%d) !" %
                        (ValueStr, Name, BitField, FilePath, LineNum + 1))

              if Value >= 2 ** SubItem['bitlength']:
                  raise Exception("Invalid configuration bit field value '%s' for '%s.%s' (File:'%s' Line:%d) !" %
                        (Value, Name, BitField, FilePath, LineNum + 1))

              ValArray = self.ValueToByteArray (Item['value'], Item['length'])
              self.UpdateBsfBitFields (SubItem, Value, ValArray)

              if Item['value'].startswith('{'):
                  Item['value'] = '{' + ', '.join('0x%02X' % i for i in ValArray) + '}'
              else:
                  BitsValue = ''.join('{0:08b}'.format(i) for i in ValArray[::-1])
                  Item['value'] = '0x%X' % (int(BitsValue, 2))
          else:
              if Item['value'].startswith('{') and  not ValueStr.startswith('{'):
                  raise Exception("Data array required for '%s' (File:'%s' Line:%d) !" % (Name, FilePath, LineNum + 1))
              Item['value'] = ValueStr

          if Name == 'PLATFORMID_CFG_DATA.PlatformId':
              PlatformId = ValueStr

        if PlatformId is None:
            raise Exception("PLATFORMID_CFG_DATA.PlatformId is missing in file '%s' !" % (DltFile))

        return Error

    def ProcessMultilines (self, String, MaxCharLength):
        Multilines = ''
        StringLength = len(String)
        CurrentStringStart = 0
        StringOffset = 0
        BreakLineDict = []
        if len(String) <= MaxCharLength:
            while (StringOffset < StringLength):
                if StringOffset >= 1:
                    if String[StringOffset - 1] == '\\' and String[StringOffset] == 'n':
                        BreakLineDict.append (StringOffset + 1)
                StringOffset += 1
            if BreakLineDict != []:
                for Each in BreakLineDict:
                    Multilines += "  %s\n" % String[CurrentStringStart:Each].lstrip()
                    CurrentStringStart = Each
                if StringLength - CurrentStringStart > 0:
                    Multilines += "  %s\n" % String[CurrentStringStart:].lstrip()
            else:
                Multilines = "  %s\n" % String
        else:
            NewLineStart = 0
            NewLineCount = 0
            FoundSpaceChar = False
            while (StringOffset < StringLength):
                if StringOffset >= 1:
                    if NewLineCount >= MaxCharLength - 1:
                        if String[StringOffset] == ' ' and StringLength - StringOffset > 10:
                            BreakLineDict.append (NewLineStart + NewLineCount)
                            NewLineStart = NewLineStart + NewLineCount
                            NewLineCount = 0
                            FoundSpaceChar = True
                        elif StringOffset == StringLength - 1 and FoundSpaceChar == False:
                            BreakLineDict.append (0)
                    if String[StringOffset - 1] == '\\' and String[StringOffset] == 'n':
                        BreakLineDict.append (StringOffset + 1)
                        NewLineStart = StringOffset + 1
                        NewLineCount = 0
                StringOffset += 1
                NewLineCount += 1
            if BreakLineDict != []:
                BreakLineDict.sort ()
                for Each in BreakLineDict:
                    if Each > 0:
                        Multilines += "  %s\n" % String[CurrentStringStart:Each].lstrip()
                    CurrentStringStart = Each
                if StringLength - CurrentStringStart > 0:
                    Multilines += "  %s\n" % String[CurrentStringStart:].lstrip()
        return Multilines

    def CreateField (self, Item, Name, Length, Offset, Struct, BsfName, Help, Option, BitsLength = None):
        PosName    = 28
        PosComment = 30
        NameLine=''
        HelpLine=''
        OptionLine=''

        if Length == 0 and Name == 'Dummy':
            return '\n'

        IsArray = False
        if Length in [1,2,4,8]:
            Type = "UINT%d" % (Length * 8)
        else:
            IsArray = True
            Type = "UINT8"

        if Item and Item['value'].startswith('{'):
            Type = "UINT8"
            IsArray = True

        if Struct != '':
            Type = Struct
            if Struct in ['UINT8','UINT16','UINT32','UINT64']:
                IsArray = True
                Unit = int(Type[4:]) // 8
                Length = Length / Unit
            else:
                IsArray = False

        if IsArray:
            Name = Name + '[%d]' % Length

        if len(Type) < PosName:
            Space1 = PosName - len(Type)
        else:
            Space1 = 1

        if BsfName != '':
            NameLine=" %s\n" % BsfName
        else:
            NameLine="\n"

        if Help != '':
            HelpLine = self.ProcessMultilines (Help, 80)

        if Option != '':
            OptionLine = self.ProcessMultilines (Option, 80)

        if Offset is None:
            OffsetStr = '????'
        else:
            OffsetStr = '0x%04X' % Offset

        if BitsLength is None:
            BitsLength = ''
        else:
            BitsLength = ' : %d' % BitsLength

        return "\n/** %s%s%s**/\n  %s%s%s%s;\n" % (NameLine, HelpLine, OptionLine, Type, ' ' * Space1, Name, BitsLength)

    def SplitTextBody (self, TextBody):
        Marker1 = '{ /* _COMMON_STRUCT_START_ */'
        Marker2 = '; /* _COMMON_STRUCT_END_ */'
        ComBody = []
        TxtBody = []
        IsCommon = False
        for Line in TextBody:
            if Line.strip().endswith(Marker1):
                Line = Line.replace(Marker1[1:], '')
                IsCommon = True
            if Line.strip().endswith(Marker2):
                Line = Line.replace(Marker2[1:], '')
                if IsCommon:
                    ComBody.append(Line)
                    IsCommon = False
                    continue
            if IsCommon:
                ComBody.append(Line)
            else:
                TxtBody.append(Line)
        return ComBody, TxtBody

    def GetStructArrayInfo (self, Input):
        ArrayStr = Input.split('[')
        Name     = ArrayStr[0]
        if len(ArrayStr) > 1:
            NumStr = ''.join(c for c in ArrayStr[-1] if c.isdigit())
            NumStr = '1000' if len(NumStr) == 0 else NumStr
            ArrayNum = int(NumStr)
        else:
            ArrayNum = 0
        return Name, ArrayNum


    def PostProcessBody (self, TextBody, IncludeEmbedOnly = True):
        NewTextBody = []
        OldTextBody = []
        IncTextBody = []
        StructBody  = []
        IncludeLine = False
        LineIsDef   = False
        EmbedFound  = False
        StructName  = ''
        ArrayVarName   = ''
        VariableName   = ''
        Count          = 0
        Level          = 0
        BaseOffset     = 0
        IsCommonStruct = False

        EmbedStructRe  = re.compile("^/\*\sEMBED_STRUCT:([\w\[\]\*]+):([\w\[\]\*]+):(\w+):(START|END)([\s\d]+)\*/([\s\S]*)")
        for Line in TextBody:
            if Line.startswith('#define '):
                IncTextBody.append(Line)
                continue

            if not Line.startswith ('/* EMBED_STRUCT:'):
                Match = False
            else:
                Match = EmbedStructRe.match(Line)
            if Match:
                ArrayMarker = Match.group(5)
                if Match.group(4) == 'END':
                    Level -= 1
                    if Level == 0:
                        Line = Match.group(6)
                else:   # 'START'
                    Level += 1
                    if Level == 1:
                        Line = Match.group(6)
                    else:
                        EmbedFound  = True
                    TagStr = Match.group(3)
                    if TagStr.startswith('TAG_'):
                        try:
                            TagVal = int(TagStr[4:], 16)
                        except:
                            TagVal = -1
                        if (TagVal >= 0) and (TagVal < self._MinCfgTagId):
                            IsCommonStruct = True

                    if Level == 1:
                        if  IsCommonStruct:
                            Suffix = ' /* _COMMON_STRUCT_START_ */'
                        else:
                            Suffix = ''
                        StructBody = ['typedef struct {%s' % Suffix]
                        StructName   = Match.group(1)
                        StructType   = Match.group(2)
                        VariableName = Match.group(3)
                        MatchOffset = re.search('/\*\*\sOffset\s0x([a-fA-F0-9]+)', Line)
                        if MatchOffset:
                            Offset = int(MatchOffset.group(1), 16)
                        else:
                            Offset = None
                        IncludeLine = True
                        BaseOffset  = Offset

                        ModifiedStructType = StructType.rstrip()
                        if ModifiedStructType.endswith(']'):
                            Idx = ModifiedStructType.index('[')
                            if ArrayMarker != ' ':
                                # Auto array size
                                OldTextBody.append('')
                                ArrayVarName = VariableName
                                if int(ArrayMarker) == 1000:
                                    Count        = 1
                                else:
                                    Count        = int(ArrayMarker) + 1000
                            else:
                                if Count < 1000:
                                    Count       += 1

                            VariableTemp = ArrayVarName + '[%d]' % (Count if Count < 1000 else Count - 1000)
                            OldTextBody[-1] = self.CreateField (None, VariableTemp, 0, Offset, ModifiedStructType[:Idx], '', 'Structure Array', '')
                        else:
                            ArrayVarName = ''
                            OldTextBody.append (self.CreateField (None, VariableName, 0, Offset, ModifiedStructType, '', '', ''))

            if IncludeLine:
                StructBody.append (Line)
            else:
                OldTextBody.append (Line)

            if Match and Match.group(4) == 'END':
                if Level == 0:
                    if (StructType != Match.group(2)) or (VariableName != Match.group(3)):
                        print ("Unmatched struct name '%s' and '%s' !"  % (StructName, Match.group(2)))
                    else:
                        if  IsCommonStruct:
                            Suffix = ' /* _COMMON_STRUCT_END_ */'
                        else:
                            Suffix = ''
                        Line = '} %s;%s\n\n\n' % (StructName, Suffix)
                        StructBody.append (Line)
                        if (Line not in NewTextBody) and (Line not in OldTextBody):
                            NewTextBody.extend (StructBody)
                    IncludeLine = False
                    BaseOffset  = 0
                IsCommonStruct = False

        if not IncludeEmbedOnly:
            NewTextBody.extend(OldTextBody)

        if EmbedFound:
            NewTextBody = self.PostProcessBody (NewTextBody, False)

        NewTextBody = IncTextBody + NewTextBody
        return NewTextBody

    def WriteHeaderFile (self, TxtBody, FileName, Type = 'h'):
        FileNameDef = os.path.basename(FileName).replace ('.', '_')
        FileNameDef = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', FileNameDef)
        FileNameDef = re.sub('([a-z0-9])([A-Z])', r'\1_\2', FileNameDef).upper()

        Lines = []
        Lines.append ("%s\n"   % GetCopyrightHeader(Type))
        Lines.append ("#ifndef __%s__\n"   % FileNameDef)
        Lines.append ("#define __%s__\n\n" % FileNameDef)
        if Type == 'h':
            Lines.append ("#pragma pack(1)\n\n")
        Lines.extend (TxtBody)
        if Type == 'h':
            Lines.append ("#pragma pack()\n\n")
        Lines.append ("#endif\n")

        # Don't rewrite if the contents are the same
        Create = True
        if os.path.exists(FileName):
            HdrFile  = open(FileName, "r")
            OrgTxt   = HdrFile.read()
            HdrFile.close()

            NewTxt   = ''.join(Lines)
            if OrgTxt == NewTxt:
                Create = False

        if Create:
            HdrFile  = open(FileName, "w")
            HdrFile.write (''.join(Lines))
            HdrFile.close()

    def CreateHeaderFile (self, HdrFileName, ComHdrFileName = ''):
        CommentLine  = ''
        LastStruct   = ''
        NextOffset   = 0
        SpaceIdx     = 0
        Offset       = 0
        FieldIdx     = 0
        LastFieldIdx = 0
        ResvOffset   = 0
        ResvIdx      = 0
        TxtBody      = []
        LineBuffer   = []
        CfgTags      = []
        InRange      = True
        LastVisible  = True

        TxtBody.append("typedef struct {\n")
        for Item in self._CfgItemList:
            # Search for CFGDATA tags
            Embed = Item["embed"].upper()
            if Embed.endswith(':START'):
                Match = re.match (r'(\w+)_CFG_DATA:TAG_([0-9A-F]+):START', Embed)
                if Match:
                    TagName = Match.group(1)
                    TagId   = int(Match.group(2), 16)
                    CfgTags.append ((TagId, TagName))

            # Only process visible items
            NextVisible = LastVisible

            if LastVisible and (Item['header'] == 'OFF'):
                NextVisible = False
                ResvOffset  = Item['offset']
            elif (not LastVisible) and Item['header'] == 'ON':
                NextVisible = True
                Name = "ReservedUpdSpace%d" % ResvIdx
                ResvIdx = ResvIdx + 1
                TxtBody.append(self.CreateField (Item, Name, Item["offset"] - ResvOffset, ResvOffset, '', '', '', ''))
                FieldIdx += 1

            if  Offset < Item["offset"]:
                if LastVisible:
                    Name = "UnusedUpdSpace%d" % SpaceIdx
                    LineBuffer.append(self.CreateField (Item, Name, Item["offset"] - Offset, Offset, '', '', '', ''))
                    FieldIdx += 1
                SpaceIdx = SpaceIdx + 1
                Offset   = Item["offset"]

            LastVisible = NextVisible

            Offset = Offset + Item["length"]
            if LastVisible:
                for Each in LineBuffer:
                    TxtBody.append (Each)
                LineBuffer = []
                Comment = Item["comment"]
                Embed = Item["embed"].upper()
                if Embed.endswith(':START') or Embed.endswith(':END'):
                    # EMBED_STRUCT: StructName : ItemName : VariableName : START|END
                    Name, ArrayNum = self.GetStructArrayInfo (Item["struct"])
                    Remaining = Item["embed"]
                    if (LastFieldIdx + 1 == FieldIdx) and (LastStruct == Name):
                        ArrayMarker  = ' '
                    else:
                        ArrayMarker  = '%d' % ArrayNum
                    LastFieldIdx = FieldIdx
                    LastStruct   = Name
                    Marker = '/* EMBED_STRUCT:%s:%s%s*/ ' % (Name, Remaining, ArrayMarker)
                    if Embed.endswith(':START') and Comment != '':
                        Marker = '/* COMMENT:%s */ \n' % Item["comment"] + Marker
                else:
                    if Embed == '':
                        Marker = ''
                    else:
                        self.Error = "Invalid embedded structure format '%s'!\n" % Item["embed"]
                        return 4

                # Generate bit fields for structure
                if len(Item['subreg']) > 0 and Item["struct"]:
                    StructType = Item["struct"]
                    StructName, ArrayNum = self.GetStructArrayInfo (StructType)
                    if (LastFieldIdx + 1 == FieldIdx) and (LastStruct == Item["struct"]):
                        ArrayMarker = ' '
                    else:
                        ArrayMarker = '%d' % ArrayNum
                    TxtBody.append('/* EMBED_STRUCT:%s:%s:%s:START%s*/\n' % (StructName, StructType, Item["cname"], ArrayMarker))
                    for SubItem in Item['subreg']:
                        Name = SubItem["cname"]
                        if Name.startswith(Item["cname"]):
                            Name = Name[len(Item["cname"]) + 1:]
                        Line = self.CreateField (SubItem, Name, SubItem["bitunit"], SubItem["offset"], SubItem['struct'], SubItem['name'], SubItem['help'], SubItem['option'], SubItem['bitlength'])
                        TxtBody.append(Line)
                    TxtBody.append('/* EMBED_STRUCT:%s:%s:%s:END%s*/\n' % (StructName, StructType, Item["cname"], ArrayMarker))
                    LastFieldIdx   = FieldIdx
                    LastStruct     = Item["struct"]
                    FieldIdx      += 1
                else:
                    FieldIdx += 1
                    Line = Marker + self.CreateField (Item, Item["cname"], Item["length"], Item["offset"], Item['struct'], Item['name'], Item['help'], Item['option'])
                    TxtBody.append(Line)

        TxtBody.append("}\n\n")

        # Handle the embedded data structure
        TxtBody  = self.PostProcessBody (TxtBody)
        ComBody, TxtBody = self.SplitTextBody (TxtBody)

        # Prepare TAG defines
        PltTagDefTxt = ['\n']
        ComTagDefTxt = ['\n']
        for TagId, TagName in sorted(CfgTags):
            TagLine = '#define  %-30s  0x%03X\n' % ('CDATA_%s_TAG' % TagName, TagId)
            if TagId < self._MinCfgTagId:
                # TAG ID < 0x100, it is a generic TAG
                ComTagDefTxt.append (TagLine)
            else:
                PltTagDefTxt.append (TagLine)
        PltTagDefTxt.append ('\n\n')
        ComTagDefTxt.append ('\n\n')

        # Write file back
        self.WriteHeaderFile (PltTagDefTxt + TxtBody, HdrFileName)
        if ComHdrFileName:
            self.WriteHeaderFile (ComTagDefTxt + ComBody, ComHdrFileName)

        return 0

    def UpdateConfigItemValue (self, Item, ValueStr):
        IsArray  = True if Item['value'].startswith('{') else False
        IsString = True if Item['value'].startswith("'") else False
        Bytes = self.ValueToByteArray(ValueStr, Item['length'])
        if IsString:
            NewValue = "'%s'" % Bytes.decode("utf-8")
        elif IsArray:
            NewValue = Bytes2Str(Bytes)
        else:
            Fmt = '0x%X' if Item['value'].startswith('0x') else '%d'
            NewValue = Fmt % Bytes2Val(Bytes)
        Item['value'] = NewValue

    def LoadDefaultFromBinaryArray (self, BinDat):
        BaseOff = 0
        for Item in self._CfgItemList:
            if Item['length'] == 0:
                continue
            if Item['find']:
                Offset = BinDat.find (Item['find'].encode())
                if Offset >= 0:
                    BaseOff = Offset
                else:
                    raise Exception ('Could not find "%s" !' % Item['find'])
            if Item['offset'] + Item['length'] > len(BinDat):
                raise Exception ('Mismatching format between DSC and BIN files !')
            ValStr = Bytes2Str(BinDat[BaseOff + Item['offset']:BaseOff + Item['offset']+Item['length']])
            self.UpdateConfigItemValue (Item, ValStr)

        self.UpdateDefaultValue()

    def GenerateBinaryArray (self):
        BinDat = bytearray()
        Offset = 0
        for Item in self._CfgItemList:
            if Item['offset'] > Offset:
                Gap = Item['offset'] - Offset
                BinDat.extend(b'\x00' * Gap)
            BinDat.extend(self.ValueToByteArray(Item['value'], Item['length']))
            Offset = Item['offset'] + Item['length']
        return BinDat

    def GenerateBinary (self, BinFileName):
        BinFile = open(BinFileName, "wb")
        BinFile.write (self.GenerateBinaryArray ())
        BinFile.close()
        return 0

    def GenerateDataIncFile (self, DatIncFileName, BinFile = None):
        # Put a prefix GUID before CFGDATA so that it can be located later on
        Prefix   = b'\xa7\xbd\x7f\x73\x20\x1e\x46\xd6\xbe\x8f\x64\x12\x05\x8d\x0a\xa8'
        if BinFile:
            Fin = open (BinFile, 'rb')
            BinDat = Prefix + bytearray(Fin.read())
            Fin.close()
        else:
            BinDat = Prefix + self.GenerateBinaryArray ()

        FileName = os.path.basename(DatIncFileName).upper()
        FileName = FileName.replace('.', '_')

        TxtLines = []

        TxtLines.append ("UINT8  mConfigDataBlob[%d] = {\n" % len(BinDat))
        Count = 0
        Line  = ['  ']
        for Each in BinDat:
            Line.append('0x%02X, ' % Each)
            Count = Count + 1
            if (Count & 0x0F) == 0:
                Line.append('\n')
                TxtLines.append (''.join(Line))
                Line  = ['  ']
        if len(Line) > 1:
            TxtLines.append (''.join(Line) + '\n')

        TxtLines.append ("};\n\n")

        self.WriteHeaderFile (TxtLines, DatIncFileName, 'inc')

        return 0

    def CheckCfgData (self):
        # Check if CfgData contains any duplicated name
        def  AddItem (Item, ChkList):
            Name = Item['cname']
            if Name in ChkList:
                return Item
            if Name not in ['Dummy', 'Reserved', 'CfgHeader', 'CondValue']:
                ChkList.append(Name)
            return None

        Duplicate = None
        ChkList   = []
        for  Item in self._CfgItemList:
            Duplicate = AddItem (Item, ChkList)
            if not Duplicate:
                for SubItem in Item['subreg']:
                    Duplicate = AddItem (SubItem, ChkList)
                    if Duplicate:
                        break
            if Duplicate:
                break
        if Duplicate:
            self.Error = "Duplicated CFGDATA '%s' found !\n" % Duplicate['cname']
            return -1
        return 0

    def PrintData (self):
        for  Item in self._CfgItemList:
            if not Item['length']:
                continue
            print ("%-10s @Offset:0x%04X  Len:%3d  Val:%s" % (Item['cname'], Item['offset'], Item['length'], Item['value']))
            for  SubItem in Item['subreg']:
                print ("  %-20s  BitOff:0x%04X  BitLen:%-3d  Val:%s" % (SubItem['cname'], SubItem['bitoffset'], SubItem['bitlength'], SubItem['value']))

    def FormatArrayValue (self, Input, Length):
        Dat = self.ValueToByteArray(Input, Length)
        return ','.join('0x%02X' % Each for Each in Dat)

    def GetItemOptionList (self, Item):
        TmpList = []
        if  Item['type'] == "Combo":
            if not Item['option'] in self._BuidinOption:
                OptList = Item['option'].split(',')
                for Option in OptList:
                    Option = Option.strip()
                    try:
                        (OpVal, OpStr) = Option.split(':')
                    except:
                        raise Exception("Invalid option format '%s' !" % Option)
                    TmpList.append((OpVal, OpStr))
        return  TmpList

    def WriteBsfStruct  (self, BsfFd, Item):
        if Item['type'] == "None":
            Space = "gPlatformFspPkgTokenSpaceGuid"
        else:
            Space = Item['space']
        Line = "    $%s_%s" % (Space, Item['cname'])
        Match = re.match("\s*(\{.+\})\s*", Item['value'])
        if Match:
            DefaultValue = self.FormatArrayValue (Match.group(1).strip(), Item['length'])
        else:
            DefaultValue = Item['value'].strip()
        if 'bitlength' in Item:
            if Item['bitlength']:
                BsfFd.write("    %s%s%4d bits     $_DEFAULT_ = %s\n" % (Line, ' ' * (64 - len(Line)), Item['bitlength'], DefaultValue))
        else:
            if Item['length']:
                BsfFd.write("    %s%s%4d bytes    $_DEFAULT_ = %s\n" % (Line, ' ' * (64 - len(Line)), Item['length'], DefaultValue))

        return self.GetItemOptionList (Item)

    def GetBsfOption (self, OptionName):
        if OptionName in self._CfgOptsDict:
            return self._CfgOptsDict[OptionName]
        else:
            return OptionName

    def WriteBsfOption  (self, BsfFd, Item):
        PcdName   = Item['space'] + '_' + Item['cname']
        WriteHelp = 0
        BsfLines  = []
        if Item['type'] == "Combo":
            if Item['option'] in self._BuidinOption:
                Options = self._BuidinOption[Item['option']]
            else:
                Options = self.GetBsfOption (PcdName)
            BsfLines.append ('    %s $%s, "%s", &%s,\n' % (Item['type'], PcdName, Item['name'], Options))
            WriteHelp = 1
        elif Item['type'].startswith("EditNum"):
            Match = re.match("EditNum\s*,\s*(HEX|DEC)\s*,\s*\((\d+|0x[0-9A-Fa-f]+)\s*,\s*(\d+|0x[0-9A-Fa-f]+)\)", Item['type'])
            if Match:
                BsfLines.append ('    EditNum $%s, "%s", %s,\n' % (PcdName, Item['name'], Match.group(1)))
                WriteHelp = 2
        elif Item['type'].startswith("EditText"):
            BsfLines.append ('    %s $%s, "%s",\n' % (Item['type'], PcdName, Item['name']))
            WriteHelp = 1
        elif Item['type'] == "Table":
            Columns = Item['option'].split(',')
            if len(Columns) != 0:
                BsfLines.append('    %s $%s "%s",' % (Item['type'], PcdName, Item['name']))
                for Col in Columns:
                    Fmt = Col.split(':')
                    if len(Fmt) != 3:
                        raise Exception("Column format '%s' is invalid !" % Fmt)
                    try:
                        Dtype = int(Fmt[1].strip())
                    except:
                        raise Exception("Column size '%s' is invalid !" % Fmt[1])
                    BsfLines.append('\n        Column "%s", %d bytes, %s' % (Fmt[0].strip(), Dtype, Fmt[2].strip()))
                BsfLines.append(',\n')
                WriteHelp = 1

        if WriteHelp  > 0:
            HelpLines = Item['help'].split('\\n\\r')
            FirstLine = True
            for HelpLine in HelpLines:
                if FirstLine:
                    FirstLine = False
                    BsfLines.append('        Help "%s"\n' % (HelpLine))
                else:
                    BsfLines.append('             "%s"\n' % (HelpLine))
            if WriteHelp == 2:
                BsfLines.append('             "Valid range: %s ~ %s"\n' % (Match.group(2), Match.group(3)))

            if len(Item['condition']) > 4:
                CondList = Item['condition'].split(',')
                Idx = 0
                for Cond in CondList:
                    Cond = Cond.strip()
                    if   Cond.startswith('#'):
                        BsfLines.insert(Idx, Cond + '\n')
                        Idx += 1
                    elif Cond.startswith('@#'):
                        BsfLines.append(Cond[1:] + '\n')

        for Line in BsfLines:
            BsfFd.write (Line)

    def WriteBsfPages (self, PageTree, BsfFd):
        BsfFd.write('\n')
        Key = next(iter(PageTree))
        for Page in PageTree[Key]:
            PageName = next(iter(Page))
            BsfFd.write('Page "%s"\n' % self._CfgPageDict[PageName])
            if len(PageTree[Key]):
                self.WriteBsfPages (Page, BsfFd)

            BsfItems = []
            for Item in self._CfgItemList:
                if Item['name'] != '':
                    if Item['page'] != PageName:
                        continue
                    if len(Item['subreg']) > 0:
                        for SubItem in Item['subreg']:
                            if SubItem['name'] != '':
                                BsfItems.append(SubItem)
                    else:
                        BsfItems.append(Item)

            BsfItems.sort(key=lambda x: x['order'])

            for Item in BsfItems:
                self.WriteBsfOption (BsfFd, Item)
            BsfFd.write("EndPage\n\n")

    def GenerateBsfFile (self, BsfFile):

        if BsfFile == '':
            self.Error = "BSF output file '%s' is invalid" % BsfFile
            return 1

        Error = 0
        OptionDict = {}
        BsfFd      = open(BsfFile, "w")
        BsfFd.write("%s\n" % GetCopyrightHeader('bsf'))
        BsfFd.write("%s\n" % self._GlobalDataDef)
        BsfFd.write("StructDef\n")
        NextOffset = -1
        for Item in self._CfgItemList:
            if Item['find'] != '':
                BsfFd.write('\n    Find "%s"\n' % Item['find'])
                NextOffset = Item['offset'] + Item['length']
            if Item['name'] != '':
                if NextOffset != Item['offset']:
                    BsfFd.write("        Skip %d bytes\n" % (Item['offset'] - NextOffset))
                if len(Item['subreg']) > 0:
                    NextOffset =  Item['offset']
                    BitsOffset =  NextOffset * 8
                    for SubItem in Item['subreg']:
                        BitsOffset += SubItem['bitlength']
                        if SubItem['name'] == '':
                            if 'bitlength' in SubItem:
                                BsfFd.write("        Skip %d bits\n" % (SubItem['bitlength']))
                            else:
                                BsfFd.write("        Skip %d bytes\n" % (SubItem['length']))
                        else:
                            Options = self.WriteBsfStruct(BsfFd, SubItem)
                            if len(Options) > 0:
                                OptionDict[SubItem['space']+'_'+SubItem['cname']] = Options

                    NextBitsOffset = (Item['offset'] + Item['length']) * 8
                    if NextBitsOffset > BitsOffset:
                        BitsGap     = NextBitsOffset - BitsOffset
                        BitsRemain  = BitsGap % 8
                        if BitsRemain:
                            BsfFd.write("        Skip %d bits\n" % BitsRemain)
                            BitsGap -= BitsRemain
                        BytesRemain = BitsGap // 8
                        if BytesRemain:
                            BsfFd.write("        Skip %d bytes\n" % BytesRemain)
                    NextOffset = Item['offset'] + Item['length']
                else:
                    NextOffset = Item['offset'] + Item['length']
                    Options = self.WriteBsfStruct(BsfFd, Item)
                    if len(Options) > 0:
                        OptionDict[Item['space']+'_'+Item['cname']] = Options
        BsfFd.write("\nEndStruct\n\n")

        BsfFd.write("%s" % self._BuidinOptionTxt)

        NameList   = []
        OptionList = []
        for Each in sorted(OptionDict):
            if OptionDict[Each] not in OptionList:
                NameList.append(Each)
                OptionList.append (OptionDict[Each])
                BsfFd.write("List &%s\n" % Each)
                for Item in OptionDict[Each]:
                    BsfFd.write('    Selection %s , "%s"\n' % (self.EvaluateExpress(Item[0]), Item[1]))
                BsfFd.write("EndList\n\n")
            else:
                # Item has idential options as other item
                # Try to reuse the previous options instead
                Idx = OptionList.index (OptionDict[Each])
                self._CfgOptsDict[Each] = NameList[Idx]

        BsfFd.write("BeginInfoBlock\n")
        BsfFd.write('    PPVer       "%s"\n' % (self._CfgBlkDict['ver']))
        BsfFd.write('    Description "%s"\n' % (self._CfgBlkDict['name']))
        BsfFd.write("EndInfoBlock\n\n")

        self.WriteBsfPages (self._CfgPageTree, BsfFd)

        BsfFd.close()
        return  Error

    def WriteDeltaLine (self, OutLines, Name, ValStr, IsArray):
        if IsArray:
            Output = '%s | { %s }' % (Name, ValStr)
        else:
            Output = '%s | 0x%X' % (Name, Array2Val(ValStr))
        OutLines.append (Output)

    def WriteDeltaFile (self, OutFile, PlatformId, OutLines):
        DltFd = open (OutFile, "w")
        DltFd.write ("%s\n"   % GetCopyrightHeader('dlt', True))
        DltFd.write ('#\n')
        DltFd.write ('# Delta configuration values for platform ID 0x%04X\n' % PlatformId)
        DltFd.write ('#\n\n')
        for Line in OutLines:
            DltFd.write ('%s\n' % Line)
        DltFd.close()

    def GenerateDeltaFile(self, DeltaFile, BinFile, Full=False):
        Fd = open (BinFile, 'rb')
        NewData = bytearray(Fd.read())
        Fd.close()
        OldData = self.GenerateBinaryArray()
        return self.GenerateDeltaFileFromBin (DeltaFile, OldData, NewData, Full)

    def GenerateDeltaFileFromBin (self, DeltaFile, OldData, NewData, Full=False):
        self.LoadDefaultFromBinaryArray (NewData)
        Lines = []
        TagName = ''
        Level = 0
        PlatformId = None
        DefPlatformId = 0

        for Item in self._CfgItemList:
            if Level == 0 and Item['embed'].endswith(':START'):
                TagName = Item['embed'].split(':')[0]
                Level += 1

            Start = Item['offset']
            End = Start + Item['length']
            FullName = '%s.%s' % (TagName, Item['cname'])
            if 'PLATFORMID_CFG_DATA.PlatformId' == FullName:
                DefPlatformId = Bytes2Val(OldData[Start:End])

            if NewData[Start:End] != OldData[Start:End] or (
                    Full and Item['name'] and (Item['cname'] != 'Dummy')):
                if TagName == '':
                    continue

                ValStr = self.FormatDeltaValue (Item)
                if not Item['subreg']:
                    Text = '%-40s | %s' % (FullName, ValStr)
                    if 'PLATFORMID_CFG_DATA.PlatformId' == FullName:
                        PlatformId = Array2Val(Item['value'])
                    else:
                        Lines.append(Text)
                else:
                    if Full:
                        Text = '## %-40s | %s' % (FullName, ValStr)
                        Lines.append(Text)

                    OldArray = OldData[Start:End]
                    NewArray = NewData[Start:End]

                    for SubItem in Item['subreg']:
                        NewBitValue = self.GetBsfBitFields(SubItem, NewArray)
                        OldBitValue = self.GetBsfBitFields(SubItem, OldArray)
                        if OldBitValue != NewBitValue or (
                                Full and Item['name'] and
                            (Item['cname'] != 'Dummy')):
                            if SubItem['cname'].startswith(Item['cname']):
                                Offset = len(Item['cname']) + 1
                                FieldName = '%s.%s' % (
                                    FullName, SubItem['cname'][Offset:])
                            ValStr = self.FormatDeltaValue (SubItem)
                            Text = '%-40s | %s' % (FieldName, ValStr)
                            Lines.append(Text)

            if Item['embed'].endswith(':END'):
                EndTagName = Item['embed'].split(':')[0]
                if EndTagName == TagName:
                    Level -= 1

        if PlatformId is None or DefPlatformId == PlatformId:
            PlatformId = DefPlatformId
            print("WARNING: 'PlatformId' configuration is same as default %d!"
                  % PlatformId)

        Lines.insert(0, '%-40s | %s\n\n' %
                     ('PLATFORMID_CFG_DATA.PlatformId', '0x%04X' % PlatformId))

        self.WriteDeltaFile (DeltaFile, PlatformId, Lines)

        return 0

    def GenerateDscFile (self, OutFile):
        DscFd = open(OutFile, "w")
        for Line in self._DscLines:
          DscFd.write (Line + '\n')
        DscFd.close ()
        return 0

def Usage():
    print ('\n'.join([
          "GenCfgData Version 0.01",
          "Usage:",
          "    GenCfgData  GENINC  BinFile             IncOutFile   [-D Macros]",
          "    GenCfgData  GENPKL  DscFile             PklOutFile   [-D Macros]",
          "    GenCfgData  GENINC  DscFile[;DltFile]   IncOutFile   [-D Macros]",
          "    GenCfgData  GENBIN  DscFile[;DltFile]   BinOutFile   [-D Macros]",
          "    GenCfgData  GENBSF  DscFile[;DltFile]   BsfOutFile   [-D Macros]",
          "    GenCfgData  GENDLT  DscFile[;BinFile]   DltOutFile   [-D Macros]",
          "    GenCfgData  GENDSC  DscFile             DscOutFile   [-D Macros]",
          "    GenCfgData  GENHDR  DscFile[;DltFile]   HdrOutFile[;ComHdrOutFile]   [-D Macros]"
          ]))

def Main():
    #
    # Parse the options and args
    #
    argc = len(sys.argv)
    if argc < 4:
        Usage()
        return 1

    GenCfgData = CGenCfgData()
    Command   = sys.argv[1].upper()
    OutFile   = sys.argv[3]

    if argc > 5 and GenCfgData.ParseMacros(sys.argv[4:]) != 0:
        raise Exception ("ERROR: Macro parsing failed !")

    FileList  = sys.argv[2].split(';')
    if len(FileList) == 2:
        DscFile   = FileList[0]
        DltFile   = FileList[1]
    elif len(FileList) == 1:
        DscFile   = FileList[0]
        DltFile   = ''
    else:
        raise Exception ("ERROR: Invalid parameter '%s' !" % sys.argv[2])

    if Command == "GENDLT" and DscFile.endswith('.dlt'):
        # It needs to expand an existing DLT file
        DltFile = DscFile
        Lines  = CGenCfgData.ExpandIncludeFiles (DltFile)
        OutTxt = ''.join ([x[0] for x in Lines])
        OutFile = open(OutFile, "w")
        OutFile.write (OutTxt)
        OutFile.close ()
        return 0;

    if not os.path.exists(DscFile):
        raise Exception ("ERROR: Cannot open file '%s' !" % DscFile)

    CfgBinFile  = ''
    if DltFile:
        if not os.path.exists(DltFile):
            raise Exception ("ERROR: Cannot open file '%s' !" % DltFile)
        if Command == "GENDLT":
            CfgBinFile = DltFile
            DltFile  = ''

    BinFile = ''
    if (DscFile.lower().endswith('.bin')) and (Command == "GENINC"):
        # It is binary file
        BinFile = DscFile
        DscFile = ''

    if BinFile:
        if GenCfgData.GenerateDataIncFile(OutFile, BinFile) != 0:
            raise Exception (GenCfgData.Error)
        return 0

    if DscFile.lower().endswith('.pkl'):
        with open(DscFile, "rb") as PklFile:
            GenCfgData.__dict__ = marshal.load(PklFile)
    else:
        if GenCfgData.ParseDscFile(DscFile) != 0:
            raise Exception (GenCfgData.Error)

        if GenCfgData.CheckCfgData() != 0:
            raise Exception (GenCfgData.Error)

        if GenCfgData.CreateVarDict() != 0:
            raise Exception (GenCfgData.Error)

        if Command == 'GENPKL':
            with open(OutFile, "wb") as PklFile:
                marshal.dump(GenCfgData.__dict__, PklFile)
            return 0

    if DltFile and Command in ['GENHDR','GENBIN','GENINC','GENBSF']:
        if GenCfgData.OverrideDefaultValue(DltFile) != 0:
            raise Exception (GenCfgData.Error)

    if GenCfgData.UpdateDefaultValue() != 0:
        raise Exception (GenCfgData.Error)

    #GenCfgData.PrintData ()

    if   sys.argv[1] == "GENBIN":
        if GenCfgData.GenerateBinary(OutFile) != 0:
            raise Exception (GenCfgData.Error)

    elif sys.argv[1] == "GENHDR":
        OutFiles = OutFile.split(';')
        BrdOutFile = OutFiles[0].strip()
        if len(OutFiles) > 1:
            ComOutFile = OutFiles[1].strip()
        else:
            ComOutFile = ''
        if GenCfgData.CreateHeaderFile(BrdOutFile, ComOutFile) != 0:
            raise Exception (GenCfgData.Error)

    elif sys.argv[1] == "GENBSF":
        if GenCfgData.GenerateBsfFile(OutFile) != 0:
            raise Exception (GenCfgData.Error)

    elif sys.argv[1] == "GENINC":
        if GenCfgData.GenerateDataIncFile(OutFile) != 0:
            raise Exception (GenCfgData.Error)

    elif sys.argv[1] == "GENDLT":
        if GenCfgData.GenerateDeltaFile(OutFile, CfgBinFile) != 0:
            raise Exception (GenCfgData.Error)

    elif sys.argv[1] == "GENDSC":
        if GenCfgData.GenerateDscFile(OutFile) != 0:
            raise Exception (GenCfgData.Error)

    else:
        raise Exception ("Unsuported command '%s' !" % Command)

    return 0

if __name__ == '__main__':
    sys.exit(Main())
